﻿/* 
* Copyright (c) 2010, The University of Texas at Austin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, 
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, 
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, 
* this list of conditions and the following disclaimer in the documentation and/or other materials 
* provided with the distribution.
*
* Neither the name of The University of Texas at Austin nor the names of its contributors may be 
* used to endorse or promote products derived from this software without specific prior written 
* permission.
* 
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Bio.Data;
using Bio.Data.Interfaces;
using Bio.Data.Providers.Structure;
using Bio.Views.Structure.Editor.Internal.Data;

namespace Bio.Views.Structure.Editor.Internal.Algorithms
{
    public class ExportDiagramToStrml
    {
        public static void ExportDiagram(StructureModelData diagram, string targetFile)
        {
            if (diagram == null ||
                diagram.Entity == null ||
                diagram.Entity.Sequence == null ||
                diagram.Entity.BasePairs == null)
                throw new ArgumentException("Structure model incomplete or empty");

            XElement diagramOutput = new XElement(RNAStructureModelXMLTags.StructureModelLabel);
            diagramOutput.Add(new XAttribute(RNAStructureModelXMLTags.StructureModelHasRenderingAttribute, true));
            diagramOutput.Add(new XAttribute(RNAStructureModelXMLTags.StructureModelSequenceLengthAttribute, diagram.Entity.Sequence.SequenceData.Count()));

            StructureModelMetadata structureModelMetadata = null;
            if (diagram.Entity.StructureModelPropertyEdits != null)
                structureModelMetadata = diagram.Entity.StructureModelPropertyEdits;
            else
                structureModelMetadata = diagram.Entity.StructureModelProperties;
                
            XElement diagramStructureModelProperties = new XElement(RNAStructureModelXMLTags.StructureModelPropertiesLabel);
            if (structureModelMetadata != null && structureModelMetadata.NucleotideRenderingMode.HasValue)
                diagramStructureModelProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideRenderingStyleLabel, structureModelMetadata.NucleotideRenderingMode.Value));

            if (structureModelMetadata != null && structureModelMetadata.NucleotideTextRendering != null)
            {
                if (structureModelMetadata.NucleotideTextRendering.Font != null)
                    diagramStructureModelProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideFontLabel, structureModelMetadata.NucleotideTextRendering.Font));

                if (structureModelMetadata.NucleotideTextRendering.Color != null)
                    diagramStructureModelProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideFillLabel, structureModelMetadata.NucleotideTextRendering.Color));

                if (structureModelMetadata.NucleotideTextRendering.FontStyle != null)
                    diagramStructureModelProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideFontStyleLabel, structureModelMetadata.NucleotideTextRendering.FontStyle));

                if (structureModelMetadata.NucleotideTextRendering.FontWeight != null)
                    diagramStructureModelProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideFontWeightLabel, structureModelMetadata.NucleotideTextRendering.FontWeight));

                if (structureModelMetadata.NucleotideTextRendering.FontSize > 0.0)
                    diagramStructureModelProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideFontSizeLabel, structureModelMetadata.NucleotideTextRendering.FontSize));
            }

            if (structureModelMetadata != null && structureModelMetadata.NucleotideCircleRendering != null)
            {
                if (structureModelMetadata.NucleotideCircleRendering.Diameter > 0.0)
                    diagramStructureModelProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideCircleDiameterLabel, structureModelMetadata.NucleotideCircleRendering.Diameter));

                if (structureModelMetadata.NucleotideCircleRendering.EdgeThickness > 0.0)
                    diagramStructureModelProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideCircleEdgeThicknessLabel, structureModelMetadata.NucleotideCircleRendering.EdgeThickness));

                if (structureModelMetadata.NucleotideCircleRendering.FillColor != null)
                    diagramStructureModelProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideCircleFillLabel, structureModelMetadata.NucleotideCircleRendering.FillColor));

                if (structureModelMetadata.NucleotideCircleRendering.EdgeColor != null)
                    diagramStructureModelProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideCircleEdgeFillLabel, structureModelMetadata.NucleotideCircleRendering.EdgeColor));

                if(structureModelMetadata.NucleotideCircleRendering.Filled.HasValue)
                    diagramStructureModelProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideCircleFilledLabel, structureModelMetadata.NucleotideCircleRendering.Filled));
            }

            if (structureModelMetadata != null && structureModelMetadata.ConnectorStyle.HasValue)
                diagramStructureModelProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelPairConnectorRenderingStyleLabel, structureModelMetadata.ConnectorStyle.Value));

            if (structureModelMetadata != null && structureModelMetadata.ConnectorVisible.HasValue)
                diagramStructureModelProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelPairConnectorVisibleLabel, structureModelMetadata.ConnectorVisible.Value));

            if (structureModelMetadata != null && structureModelMetadata.ConnectorRendering != null)
            {
                if (structureModelMetadata.ConnectorRendering.Size > 0.0)
                    diagramStructureModelProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelPairConnectorSizeLabel, structureModelMetadata.ConnectorRendering.Size));

                if (structureModelMetadata.ConnectorRendering.EdgeThickness > 0.0)
                    diagramStructureModelProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelPairConnectorStrokeThicknessLabel, structureModelMetadata.ConnectorRendering.EdgeThickness));

                if (structureModelMetadata.ConnectorRendering.FillColor != null)
                    diagramStructureModelProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelPairConnectorFillColorLabel, structureModelMetadata.ConnectorRendering.FillColor));

                if (structureModelMetadata.ConnectorRendering.StrokeColor != null)
                    diagramStructureModelProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelPairConnectorStrokeColorLabel, structureModelMetadata.ConnectorRendering.StrokeColor));

                if (structureModelMetadata.ConnectorRendering.Filled.HasValue)
                    diagramStructureModelProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelPairConnectorFilledLabel, structureModelMetadata.ConnectorRendering.Filled.Value));
            }
            diagramOutput.Add(diagramStructureModelProperties);
            
            StringBuilder sequenceStrBuilder = new StringBuilder();
            foreach (var nt in diagram.Entity.Sequence.SequenceData)
                sequenceStrBuilder.Append(nt.Value);

            XElement diagramSequence = new XElement(RNAStructureModelXMLTags.StructureModelSequenceLabel, sequenceStrBuilder.ToString());
            diagramOutput.Add(diagramSequence);

            SequenceRendering rendering = null;
            if (diagram.Entity.SequenceRenderingEdits != null)
            {
                rendering = diagram.Entity.SequenceRenderingEdits;
            }
            else
            {
                IExtensibleProperties diagramSequenceProps = diagram.Entity.Sequence as IExtensibleProperties;
                if (diagramSequenceProps != null && 
                diagramSequenceProps.DoesExtendedPropertyExist(SequenceMetadata.Key) && 
                diagramSequenceProps.GetExtendedProperty<SequenceMetadata>(SequenceMetadata.Key).Data.ContainsKey(SequenceRendering.Key))
                    rendering = (SequenceRendering)diagramSequenceProps.GetExtendedProperty<SequenceMetadata>(SequenceMetadata.Key).Data[SequenceRendering.Key];
            }
            
            if (rendering != null)
            {
                XElement diagramRendering = new XElement(RNAStructureModelXMLTags.StructureModelRenderingLabel);
                var renderingMap = from idx in rendering.RenderingMap.Keys
                                   select new XElement(RNAStructureModelXMLTags.StructureModelRenderingVectorLabel,
                                               new XElement(RNAStructureModelXMLTags.StructureModelRenderingVectorNucleotideIndexLabel, (idx + 1)),
                                               new XElement(RNAStructureModelXMLTags.StructureModelRenderingVectorXLabel, Math.Round(rendering.RenderingMap[idx].StartX, 2)),
                                               new XElement(RNAStructureModelXMLTags.StructureModelRenderingVectorYLabel, Math.Round(rendering.RenderingMap[idx].StartY, 2)));//,
                                               //new XElement(RNAStructureModelXMLTags.StructureModelRenderingVectorMagnitudeLabel, rendering.RenderingMap[idx].Magnitude),
                                               //new XElement(RNAStructureModelXMLTags.StructureModelRenderingVectorRayAngleLabel, rendering.RenderingMap[idx].RayAngle));
                diagramRendering.Add(renderingMap.ToArray());
                diagramOutput.Add(diagramRendering);
            }

            XElement diagramPairs = new XElement(RNAStructureModelXMLTags.StructureModelBasePairsLabel);
            foreach (var pair in diagram.Entity.BasePairs)
            {
                XElement diagramPair = new XElement(RNAStructureModelXMLTags.StructureModelPairLabel);
                diagramPair.Add(new XElement(RNAStructureModelXMLTags.StructureModelPairFivePrimeIndexLabel, (pair.FivePrimeIndex + 1)));
                diagramPair.Add(new XElement(RNAStructureModelXMLTags.StructureModelPairThreePrimeIndexLabel, (pair.ThreePrimeIndex + 1)));

                IExtensibleProperties pairProps = pair as IExtensibleProperties;
                if (pairProps != null)
                {
                    BasePairMetadata bpMetadata = null;
                    if (diagram.Entity.BasePairPropertyEdits.ContainsKey(pair.FivePrimeIndex))
                    {
                        bpMetadata = diagram.Entity.BasePairPropertyEdits[pair.FivePrimeIndex];
                    }
                    else
                    {
                        if(pairProps.DoesExtendedPropertyExist(BasePairMetadata.Key))
                            bpMetadata = pairProps.GetExtendedProperty<BasePairMetadata>(BasePairMetadata.Key);
                    }

                    if (bpMetadata != null)
                    {
                        if (bpMetadata.ConnectorVisible.HasValue)
                            diagramPair.Add(new XElement(RNAStructureModelXMLTags.StructureModelPairConnectorVisibleLabel, bpMetadata.ConnectorVisible.Value));

                        if (bpMetadata.ConnectorStyle.HasValue)
                            diagramPair.Add(new XElement(RNAStructureModelXMLTags.StructureModelPairConnectorRenderingStyleLabel, bpMetadata.ConnectorStyle.Value));

                        if (bpMetadata.ConnectorRendering != null)
                        {
                            XElement diagramPairConnector = new XElement(RNAStructureModelXMLTags.StructureModelPairConnectorLabel);

                            if (bpMetadata.ConnectorRendering.FillColor != null)
                                diagramPairConnector.Add(new XElement(RNAStructureModelXMLTags.StructureModelPairConnectorFillColorLabel, bpMetadata.ConnectorRendering.FillColor));

                            if (bpMetadata.ConnectorRendering.StrokeColor != null)
                                diagramPairConnector.Add(new XElement(RNAStructureModelXMLTags.StructureModelPairConnectorStrokeColorLabel, bpMetadata.ConnectorRendering.StrokeColor));

                            if (bpMetadata.ConnectorRendering.Size > 0.0)
                                diagramPairConnector.Add(new XElement(RNAStructureModelXMLTags.StructureModelPairConnectorSizeLabel, bpMetadata.ConnectorRendering.Size));

                            if (bpMetadata.ConnectorRendering.EdgeThickness > 0.0)
                                diagramPairConnector.Add(new XElement(RNAStructureModelXMLTags.StructureModelPairConnectorStrokeThicknessLabel, bpMetadata.ConnectorRendering.EdgeThickness));

                            if (bpMetadata.ConnectorRendering.Filled.HasValue)
                                diagramPairConnector.Add(new XElement(RNAStructureModelXMLTags.StructureModelPairConnectorFilledLabel, bpMetadata.ConnectorRendering.Filled));

                            diagramPair.Add(diagramPairConnector);
                        }

                        if (bpMetadata.NucleotideRenderingProperties != null)
                        {
                            XElement diagramPairNtProperties = new XElement(RNAStructureModelXMLTags.StructureModelPairNucleotideLabel);
                            if (bpMetadata.NucleotideRenderingProperties.TextRendering != null)
                            {
                                if (bpMetadata.NucleotideRenderingProperties.TextRendering.Font != null)
                                    diagramPairNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideFontLabel, bpMetadata.NucleotideRenderingProperties.TextRendering.Font));

                                if (bpMetadata.NucleotideRenderingProperties.TextRendering.Color != null)
                                    diagramPairNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideFillLabel, bpMetadata.NucleotideRenderingProperties.TextRendering.Color));

                                if (bpMetadata.NucleotideRenderingProperties.TextRendering.FontStyle != null)
                                    diagramPairNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideFontStyleLabel, bpMetadata.NucleotideRenderingProperties.TextRendering.FontStyle));

                                if (bpMetadata.NucleotideRenderingProperties.TextRendering.FontWeight != null)
                                    diagramPairNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideFontWeightLabel, bpMetadata.NucleotideRenderingProperties.TextRendering.FontWeight));

                                if (bpMetadata.NucleotideRenderingProperties.TextRendering.FontSize > 0.0)
                                    diagramPairNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideFontSizeLabel, bpMetadata.NucleotideRenderingProperties.TextRendering.FontSize));
                            }

                            if (bpMetadata.NucleotideRenderingProperties.CircleRendering != null)
                            {
                                if (bpMetadata.NucleotideRenderingProperties.CircleRendering.Filled.HasValue)
                                    diagramPairNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideCircleFilledLabel, bpMetadata.NucleotideRenderingProperties.CircleRendering.Filled));

                                if (bpMetadata.NucleotideRenderingProperties.CircleRendering.Diameter > 0.0)
                                    diagramPairNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideCircleDiameterLabel, bpMetadata.NucleotideRenderingProperties.CircleRendering.Diameter));

                                if (bpMetadata.NucleotideRenderingProperties.CircleRendering.EdgeColor != null)
                                    diagramPairNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideCircleEdgeFillLabel, bpMetadata.NucleotideRenderingProperties.CircleRendering.EdgeColor));

                                if (bpMetadata.NucleotideRenderingProperties.CircleRendering.FillColor != null)
                                    diagramPairNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideCircleFillLabel, bpMetadata.NucleotideRenderingProperties.CircleRendering.FillColor));

                                if (bpMetadata.NucleotideRenderingProperties.CircleRendering.EdgeThickness > 0.0)
                                    diagramPairNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideCircleEdgeThicknessLabel, bpMetadata.NucleotideRenderingProperties.CircleRendering.EdgeThickness));
                            }

                            if (bpMetadata.NucleotideRenderingProperties.Visible.HasValue)
                                diagramPairNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideVisibilityLabel, bpMetadata.NucleotideRenderingProperties.Visible));

                            if (bpMetadata.NucleotideRenderingProperties.IsSubstituted)
                                diagramPairNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideSubstitutionLabel, bpMetadata.NucleotideRenderingProperties.SubstitutionCharacter));

                            if (bpMetadata.NucleotideRenderingProperties.Style.HasValue)
                                diagramPairNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideRenderingStyleLabel, bpMetadata.NucleotideRenderingProperties.Style));

                            diagramPair.Add(diagramPairNtProperties);
                        }
                    }
                }
                diagramPairs.Add(diagramPair);
            }
            diagramOutput.Add(diagramPairs);

            XElement sequenceNtProperties = new XElement(RNAStructureModelXMLTags.StructureModelNucleotidePropertiesLabel);
            for (int i = 0; i < diagram.Entity.Sequence.SequenceData.Count(); i++)
            {
                XElement diagramNtProperties = new XElement(RNAStructureModelXMLTags.StructureModelNucleotideLabel);
                diagramNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideIndexLabel, (i + 1)));
                
                IExtensibleProperties ntProperties = diagram.Entity.Sequence.SequenceData[i] as IExtensibleProperties;
                if (ntProperties != null)
                {
                    NucleotideMetadata ntMetadata = null;
                    if (diagram.Entity.NucleotidePropertyEdits.ContainsKey(i))
                    {
                        ntMetadata = diagram.Entity.NucleotidePropertyEdits[i];
                    }
                    else
                    {
                        if(ntProperties.DoesExtendedPropertyExist(NucleotideMetadata.Key))
                            ntMetadata = ntProperties.GetExtendedProperty<NucleotideMetadata>(NucleotideMetadata.Key);
                    }

                    if (ntMetadata != null)
                    {
                        if (ntMetadata.TextRendering != null)
                        {
                            if (ntMetadata.TextRendering.Font != null)
                                diagramNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideFontLabel, ntMetadata.TextRendering.Font));

                            if (ntMetadata.TextRendering.Color != null)
                                diagramNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideFillLabel, ntMetadata.TextRendering.Color));

                            if (ntMetadata.TextRendering.FontStyle != null)
                                diagramNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideFontStyleLabel, ntMetadata.TextRendering.FontStyle));

                            if (ntMetadata.TextRendering.FontWeight != null)
                                diagramNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideFontWeightLabel, ntMetadata.TextRendering.FontWeight));

                            if (ntMetadata.TextRendering.FontSize > 0.0)
                                diagramNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideFontSizeLabel, ntMetadata.TextRendering.FontSize));
                        }

                        if (ntMetadata.CircleRendering != null)
                        {
                            if (ntMetadata.CircleRendering.Filled.HasValue)
                                diagramNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideCircleFilledLabel, ntMetadata.CircleRendering.Filled));

                            if (ntMetadata.CircleRendering.Diameter > 0.0)
                                diagramNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideCircleDiameterLabel, ntMetadata.CircleRendering.Diameter));

                            if (ntMetadata.CircleRendering.EdgeColor != null)
                                diagramNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideCircleEdgeFillLabel, ntMetadata.CircleRendering.EdgeColor));

                            if (ntMetadata.CircleRendering.FillColor != null)
                                diagramNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideCircleFillLabel, ntMetadata.CircleRendering.FillColor));

                            if (ntMetadata.CircleRendering.EdgeThickness > 0.0)
                                diagramNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideCircleEdgeThicknessLabel, ntMetadata.CircleRendering.EdgeThickness));
                        }

                        if (ntMetadata.Visible.HasValue)
                            diagramNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideVisibilityLabel, ntMetadata.Visible));

                        if (ntMetadata.IsSubstituted)
                            diagramNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideSubstitutionLabel, ntMetadata.SubstitutionCharacter));

                        if (ntMetadata.Style.HasValue)
                            diagramNtProperties.Add(new XElement(RNAStructureModelXMLTags.StructureModelNucleotideRenderingStyleLabel, ntMetadata.Style));
                    }
                }

                sequenceNtProperties.Add(diagramNtProperties);
            }

            diagramOutput.Add(sequenceNtProperties);

            XDocument diagramXMLDoc = new XDocument();
            diagramXMLDoc.Add(diagramOutput);
            diagramXMLDoc.Save(targetFile);
        }
    }
}
